package cloud.tianai.nio.channel;

import cloud.tianai.nio.handler.ChannelPipeline;
import lombok.SneakyThrows;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.List;

public class NioServerSocketChannel extends AbstractNioChannel {

    /**
     * 多路复用器提供者
     */
    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

    /**
     * 开启一个 java ServerSocketChannel
     * @param provider
     * @return
     */
    private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            return null;
        }
    }

    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
    @SneakyThrows(IOException.class)
    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);

    }

    /**
     * 覆盖，因为可以确定返回的是ServerSocketChannel
     * @return
     */
    @Override
    public ServerSocketChannel getJavaChannel() {
        return (ServerSocketChannel) super.getJavaChannel();
    }

    /**
     * 绑定端socket
     * @param address
     * @throws Exception
     */
    @Override
    public void bind(SocketAddress address) throws IOException{
        getJavaChannel().bind(address);
    }

    @Override
    public void connect(SocketAddress address) throws IOException {
        // ignore
    }

    @Override
    public void write(ByteBuffer buffer) {
        // ignore
    }


    @Override
    public void doHandle(SelectionKey k) throws IOException{
        final ChannelPipeline pipeline = this.getPipeline();
        // 实际读取信息，由子类实现
        SocketChannel ch = getJavaChannel().accept();
        if (ch != null) {
            // 将读取到的nio管道交给handler执行
            try {
                pipeline.fireChannelRead(new NioSocketChannel(this, ch));
            } catch (Exception e) {
                // 关闭管道
                ch.close();
                throw e;
            }
        }
    }
}
